/**
 * 
 */
package org.framework2.utils;

import java.io.IOException;
import java.io.Serializable;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * <p>
 * HTTP客户端，用于发起HTTP请求
 * </p>
 * @see 
 * @author ahli
 * @version WebServerTestMockV1, 2010-8-19
 * @since WebServerTestMockV1
 */
public class HttpClientUtil extends Thread{
	
	/**
	 * 日志对象
	 */
	private static final Log log = LogFactory.getLog(HttpClientUtil.class);
	
	/**
	 * 定时任务执行器
	 */
	final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(40);
	
	/**
	 * 回执容器
	 */
	final static HttpInvokerDataContainer container = new HttpInvokerDataContainer();
	
	/**
	 * 临时回执容器
	 */
	final static Deque<HttpInvokeData> queue = new LinkedBlockingDeque<HttpInvokeData>(1000);
	/*
	 * 类加载的时候，启动两个定时任务；
	 *  1、从回执容器中将达到回执时间的数据弹出，放到临时内存队列中。
	 *  2、多线程并发遍历回执内存队列，如果有记录，则进行回执操作。
	 */
	static{
		
		// 启动
		scheduler.scheduleAtFixedRate(new Runnable() {
			
			@Override
			public void run() {
				LogUtils.initLogID();
				try{
					if(log.isDebugEnabled()){
						log.debug(LogUtils.format("container.getAndRemove()"));
					}
					Queue<HttpInvokeData> _queue = container.getAndRemove(System.currentTimeMillis());
					if(null != _queue && _queue.size() > 0){
						if(log.isDebugEnabled()){
							log.debug(LogUtils.format("_queue.size: ") + _queue.size());
						}
						queue.addAll(_queue);
					}
				}catch(Exception e){
					log.error(LogUtils.format(e.toString()));
				}
			}
		}, 1, 1, TimeUnit.SECONDS);
		
		// 启动
		scheduler.scheduleWithFixedDelay(new Runnable() {
			
			@Override
			public void run() {
				LogUtils.initLogID();
				try{
					HttpInvokeData hid;
					while(null != (hid = queue.poll())){
						if(log.isInfoEnabled()){
							log.info(LogUtils.format(hid.toString()));
						}
						try {
							String ret = HttpInvoke.postData(hid.getUrl(), hid.getParameterMap());
							if(log.isInfoEnabled()){
								log.info(LogUtils.format(ret));
							}
						} catch (IOException e) {
							log.error(LogUtils.format(e.toString()));
						}
					}
				}catch(Exception e){
					log.error(LogUtils.format(e.toString()));
				}
				
			}
		}, 200, 200, TimeUnit.MILLISECONDS);
		
	}
	
	public static void post(String url, int sleep, Map<String, Object> parameterMap){
		container.add(System.currentTimeMillis() + sleep, new HttpInvokeData(parameterMap, url));
	}
}


/**
 * <p>
 * HTTP调用请求数据容器
 * </p>
 * @see 
 * @author ahli
 * @version WebServerTestMockV1, 2010-8-24
 * @since WebServerTestMockV1
 */
class HttpInvokerDataContainer{
	
	/**
	 * 容器
	 */
	private HashMap<Key, HttpInvokeData> container = new HashMap<Key, HttpInvokeData>();
	
	public void add(Long time, HttpInvokeData data){
		synchronized (container) {
			container.put(new Key(time, IDUtils.uuid()), data);
		}
	}
	
	@SuppressWarnings("unchecked")
	public Queue<HttpInvokeData> getAndRemove(Long time){
		try{
			if(container.isEmpty()){
				return null;
			}
			HashMap<Key, HttpInvokeData> _container = null;
			synchronized (container) {
				_container = (HashMap<Key, HttpInvokeData>)container.clone();
			}
			long curTime = System.currentTimeMillis();
			Queue<HttpInvokeData> queue = null;
			if(null != _container){
				Set<Key> timeSet = _container.keySet();
				for(Key key : timeSet){
					if(key.getTime() < curTime){
						synchronized (container) {
							if(null == queue){
								queue = new LinkedList<HttpInvokeData>();
							}
							queue.add(container.remove(key));
						}
					}
				}
			}
			return queue;
		}catch(RuntimeException e){
			e.printStackTrace();
			throw e;
		}
	}
}

/**
 * <p>
 * 主键KEY
 * </p>
 * @see 
 * @author aohai.li
 * @version CMSV100R001DB0SP05, 2010-9-16
 * @since CMSV100R001DB0SP05
 */
class Key implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	private long time = 0;
	private String uuid;
	
	public Key(long time, String uuid) {
		super();
		this.time = time;
		this.uuid = uuid;
	}
	public long getTime() {
		return time;
	}
	public void setTime(long time) {
		this.time = time;
	}
	public String getUuid() {
		return uuid;
	}
	public void setUuid(String uuid) {
		this.uuid = uuid;
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
		return result;
	}
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Key other = (Key) obj;
		if (uuid == null) {
			if (other.uuid != null)
				return false;
		} else if (!uuid.equals(other.uuid))
			return false;
		return true;
	}
	
	
}

class HttpInvokeData implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * 请求参数
	 */
	private Map<String, Object> parameterMap = new HashMap<String, Object>();
	
	/**
	 * 请求地址
	 */
	private String url;
	
	public HttpInvokeData(Map<String, Object> parameterMap,
			String url) {
		super();
		this.parameterMap = parameterMap;
		this.url = url;
	}

	public Map<String, Object> getParameterMap() {
		return parameterMap;
	}

	public String getUrl() {
		return url;
	}
}
